// Original script by Mandoble, heavily tweaked and modified by Tyrghen
//#define __FORCE_TRACE__
//#define __FORCE_DEBUG__
//#define __FORCE_INFO__
#define THIS_FILE "editor\fn_landChopper.sqf"
#include <tmf_constants.h>
#include <tmf_macros.h>

TRACE_1("Start",_this);
TMFPROFILERSTART;

private ["_heli","_landing_pos","_engine_off","_auto_take_off","_pos","_objs","_landed","_pilot","_time_out","_speed","_speed_planar","_dist_old","_dist","_dir","_grp","_wp","_idx","_ps","_angh","_height"];
DEFAULT_PARAM(_heli,0,objNull);
DEFAULT_PARAM(_landing_pos,1,[]);
DEFAULT_PARAM(_engine_off,2,false);
DEFAULT_PARAM(_auto_take_off,3,false);
DEFAULT_PARAM(_force_script_landing,4,false);

if (!(isNull _heli) && {local (driver _heli)}) then {
	_pos = [];
	_wp = [];
	_idx = 0;
	_landed = false;
	// Landing Pos is a marker
	if (IS_STRING(_landing_pos) && {IS_MARKER(_landing_pos)}) then {
		_pos = getMarkerPos _landing_pos;
		DEBUG_1("Marker Pos",ARR_2(_pos,_landing_pos));
	};
	// Landing Pos is a Waypoint
	if ((count _pos) == 0 && {IS_ARRAY(_landing_pos)} && {IS_GROUP((_landing_pos select 0))}) then {
		_pos = waypointPosition _landing_pos;
		DEBUG_1("Waypoint Pos",ARR_2(_pos,_landing_pos));
	};
	// Landing Pos is a position
	if ((count _pos) == 0 && {IS_ARRAY(_landing_pos)} && {(count _landing_pos) >= 2}) then {
		_pos = _landing_pos;
		DEBUG_1("Position Pos",ARR_2(_pos,_landing_pos));
	};
	// No landing Pos was given we try to find an helipad
	if ((count _pos) == 0 && {IS_ARRAY(_landing_pos)} && {(count _landing_pos) == 0}) then {
		_objs =  [((getPos _heli) select 0),((getPos _heli) select 1)] nearObjects ["Helipad_base_F", 300];
		{
			if ((count _pos == 0) || {(_x distance _heli) < (_pos distance _heli)}) then {
				_pos = getPos _x;
			};
		} foreach _objs;
		DEBUG_1("Helipad Pos",ARR_2(_pos,_objs));
	};
	// No landing position, we try to extract from the current waypoint
	if ((count _pos) == 0 && {IS_ARRAY(_landing_pos)} && {(count _landing_pos) == 0}) then {
		_grp = group (driver _heli);
		_idx = currentWaypoint _grp;
		if (_idx > 0) then {
			_wp = [_grp, _idx];
			_pos = waypointPosition _wp;
		};
		DEBUG_1("Current waypoint Pos",ARR_2(_pos,_idx));
	};
	if (count _pos >= 2) then {
		_heli setVariable ["landChopper", "moving",true];
		if (alive _heli) then {
			sleep 1;
			_pos set [2,0.5];
			_landed = False;
			_speed = 3;
			_speed_planar = 0.2;
			_dist_old = 1000;
			_dist = [_heli, _pos] call FFUNC(distTo);
			_dir = [_heli, _pos] call FFUNC(dirTo);
			DEBUG_1("Moving to Landing Pos",ARR_4(getPos _heli, _pos, _dist, _dir));
			// Move above position
			_pilot = (driver _heli);
			if (_dist > 2) then {
				_ps = _heli worldToModel _pos;
				_angh = (_ps select 0) atan2 (_ps select 1);
				// Set direction right
				for "_i" from 0 to (abs _angh) - 1 do {
					if (!(alive _pilot) || (damage _heli >= 0.5)) exitWith {};
					_heli setDir ((getDir _heli)+abs(_angh)/_angh);
					_heli setVelocity [0,0,0];
					sleep 0.05;
				}; 
				DEBUG_1("Direction set",ARR_3(getPos _heli, getDir _heli, _dir));
				// Move to position
				_dist = [_heli, _pos] call FFUNC(distTo);
				_time_out = time + 400;
				_dir = [_heli, _pos] call FFUNC(dirTo);
				(driver _heli) doMove _pos;
				(driver _heli) disableAI "MOVE";
				while {(_dist_old >= _dist) && (_dist > 2) && (alive (driver _heli)) && (damage _heli < 0.5) && (canMove _heli) && (_time_out > time)} do {
					if (_speed < 10) then {_speed = _speed + 0.3};
					//diag_log ["Moving",_speed,_dir,_dist,_dist_old,velocity _heli,getPos _heli];
					_dist = [_heli, _pos] call FFUNC(distTo);
					_heli setVelocity [sin(_dir)*_speed, cos(_dir)*_speed, 0];
					//_speed_planar
					// _ps = [(((getPos _heli) select 0) + sin(_dir)*_speed_planar), (((getPos _heli) select 1) + cos(_dir)*_speed_planar), ((getPos _heli) select 2)];
					// _heli setPos _ps;
					//_heli setVectorUp [0,0,0.97];
					sleep 0.001;
					_dist_old = _dist;
				};
				(driver _heli) enableAI "MOVE";
				DEBUG_1("Above position",(getPos _heli));
			};
		};
		if (alive _heli && !_force_script_landing) then {
			sleep 1;
			DEBUG_1("Try automated landing",(getPos _heli));
			_heli setVariable ["landChopper", "landing",true];
			// First try an automated landing
			_ps = getPos _heli;
			_time_out = time + 10;
			_heli land "LAND";
			_height = _ps select 2;
			while { _time_out > time } do {
				sleep 3;
				if ((_heli distance _ps) > 2 && abs(((getPos _heli) select 2) - _height) > 1) then {
					_time_out = time + 10;
					_height = ((getPos _heli) select 2);
				};
			};
			if (((getPos _heli) select 2) < 1) then {
				DEBUG_1("Automated landing success",(getPos _heli));
				if (_engine_off) then { (driver _heli) action ["ENGINEOFF", _heli]; };
				_landed = true;
			};
		};
		_pilot = (driver _heli);
		if (!_landed && {alive (driver _heli) && (damage _heli < 0.5) && (canMove _heli)}) then {
			_heli setVariable ["landChopper", "landing",true];
			_time_out = time + 400;
			_speed = -7;
			DEBUG_1("Start scripted landing",(getPos _heli));
			_pilot disableAI "MOVE";
			_height = (getPos _heli) select 2;
			while {(isEngineOn _heli) && (alive (driver _heli)) && (damage _heli < 0.5) && (canMove _heli) && (! _landed) && (_time_out > time)} do {
				if (((getPos _heli select 2) > ((_pos select 2) + 2)) And ((_time_out - time) > 300)) then {
					_heli setDir _dir;
					if ((getPos _heli select 2) > ((_pos select 2) + 8)) then {
						// ARMA 3 SUCKS at setVelocity when the AI drives
						_speed = 0 - (((getPos _heli select 2) / 7) max 7);
						_heli setVelocity [0,0,_speed];
						// So I have to script the position change if I want the damn thing to work in MP...
						_ps = getPos _heli;
						_speed = ((((_ps select 2) / 3) min 0.4) max 0.2);
						_height = _height - _speed;
						_ps set [2, _height];
						_heli setPos _ps;
						//diag_log ["Landing",_ps, _speed, _height, velocity _heli, speed _heli];
					} else {
						_heli setVelocity [0,0,4*((((getPos _heli select 2)-(_pos select 2))/-5) min -1)];
						// So I have to script the position change if I want the damn thing to work in MP...
						_ps = getPos _heli;
						_speed = ((((_ps select 2) / 3) min 0.1) max 0.02);
						_height = _height - _speed;
						_ps set [2, _height];
						_heli setPos _ps;
						//diag_log ["Landing2",_ps, _speed, _height, velocity _heli, speed _heli];
					};
				} else {
					if (_engine_off) then { (driver _heli) action ["ENGINEOFF", _heli]; };
					_landed = True;			
				};
				//diag_log ["Landing",_speed,_dir,_dist,_dist_old,velocity _heli];
				sleep 0.002;
			};
		};
		
		if (!(alive (driver _heli)) || (damage _heli >= 0.5) || !(canMove _heli)) then {
			_heli setVariable ["landChopper", "damaged",true];
			DEBUG_1("Damaged",(getPos _heli));
		} else {
			DEBUG_1("Landed",(getPos _heli));
			_heli setVariable ["landChopper", "landed",true];
			if (_engine_off) then { (driver _heli) action ["ENGINEOFF", _heli]; };
			if (({isPlayer _x} count (crew _heli)) > 0) then {
				_pilot disableAI "MOVE";
				(driver _heli) doMove _pos;
				doStop (driver _heli);
				(driver _heli) stop true;
				for "_i" from 0 to 199 do {
					_heli setVelocity [0,0,-0.6];
					sleep 0.02;
					if (((velocity _heli) vectorDistance [0,0,0]) <= 0.1) exitWith {};
				};
				DEBUG_1("Waiting for disembark",(getPos _heli));
				_heli setVariable ["landChopper", "disembark",true];
				while {(alive (driver _heli)) && (damage _heli < 0.5) && (({isPlayer _x} count (crew _heli)) > 0)} do {
					_heli setVelocity [0,0,-0.6];
					if (_engine_off && isEngineOn _heli) then { (driver _heli) action ["ENGINEOFF", _heli]; };
					sleep 0.01;
				};
			};
			sleep 3;
			_pilot enableAI "MOVE";
			_heli setVariable ["landChopper", "takingoff",true];
			if (_auto_take_off && {(alive (driver _heli) && alive _heli && canMove _heli)}) then {
				_pos = [];
				DEBUG_1("Taking off",(getPos _heli));
				if (_engine_off) then {_pilot action ["ENGINEON", _heli];};
				// We try to extract the initial position from chopper variables
				_grp = group (driver _heli);
				_pos = _heli getVariable ["start_position",[]];
				if (count _pos == 0) then {
					// We check if there is a next waypoint
					if (_idx > 0 && {(count (waypoints _grp)) > _idx}) then {
						_wp = (waypoints _grp) select (_idx + 1);
						_grp setCurrentWaypoint _wp;
						_pos = waypointPosition _wp;
					};
				};
				if (count _pos == 0) then {
					// We try to go back to the first waypoint
					if (count (waypoints _grp) > 0) then {
						_wp = (waypoints _grp) select 0;
						_pos = waypointPosition _wp;
					};
				};
				if (count _pos == 0) then {
					// We pick a random direction
					_pos = [] call FFUNC(getRanOuterAirPoint);
				};
				if (count _pos > 0) then {
					_heli flyinHeight 100;
					(driver _heli) doMove _pos;
					waituntil {
						sleep 5;
						(driver _heli) doMove _pos;
						((speed _heli) > 5)
					};
					_heli setVariable ["landChopper", "moving",true];
					DEBUG_1("En route",(getPos _heli));
					_ps = getPos _heli;
					_time_out = time + 30;
					waituntil { _time_out < time };
					if ((_heli distance _ps) < 100) then {
						_pilot doMove (wayPointPosition [_grp, _wp]);
					};
					waituntil { sleep 15; (_heli distance _pos) < 200 };
					waituntil { sleep 3;(unitReady (driver _heli)) || (!(alive _heli)) };
					sleep 1;
					_heli land "LAND";
				};
			} else {
				_pilot = (driver _heli);
				_grp = group _pilot;
				_wp = CurrentWaypoint _grp;
				if (count (waypoints _grp) >= _wp) then {
					_grp setCurrentWaypoint [_grp, _wp];
					_pilot doMove (wayPointPosition [_grp, _wp]);
					_ps = getPos _heli;
					_time_out = time + 30;
					waituntil { _time_out < time };
					if ((_heli distance _ps) < 100) then {
						_pilot doMove (wayPointPosition [_grp, _wp]);
					};
				};
			};
		};
	} else {
		ERROR_1("No landing position",_this);
	};
} else {
	ERROR_1("No chopper",_this);
};


TRACE("End");
TMFPROFILERSTOP;